package cog.support.alibaba.zookeeper;

import cog.support.util.common.StringKit;
import com.alibaba.fastjson.JSONObject;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.retry.RetryNTimes;
import org.apache.zookeeper.CreateMode;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.*;
import java.util.ArrayList;
import java.util.List;

/**
 * zookpeer工具类
 *
 * @author 陈杰
 * @since 2017/7/16 1:46
 *
 *
 * Copyright ChenJie(wuzhiyun@aliyun.com)
 */
public class CuratorClient {
    private static Logger logger = LoggerFactory.getLogger(CuratorClient.class);

    private static List<CuratorFramework> clientList = new ArrayList<>();

    private CuratorFramework client;

    private String connectionString;

    private String namespace;

    public CuratorClient(String connectionString,String namespace){
        this.namespace = namespace;
        this.connectionString = connectionString;
    }

    private void init(){
        this.client = CuratorFrameworkFactory.builder()
                .connectString(connectionString)
                .sessionTimeoutMs(30000)
                .retryPolicy(new RetryNTimes(3, 1000))
                .connectionTimeoutMs(30000)
                .namespace(this.namespace)
                .build();
        this.client.start();
        this.clientList.add(this.client);
    }

    public static void stop(){
        if(clientList.size()>0){
            for(CuratorFramework cli : clientList){
                try {
                    cli.close();
                }catch (Exception ignored){}
            }
            logger.info("断开zookeeper连接!");
        }
    }

    /**
     * 获取节点下的子节点路径
     *
     * @param path 父节点路径
     * */
    public List<String> getChildren(String path){
        try {
            return this.client.getChildren().forPath(path);
        } catch (Exception e) {
            logger.info("获取子节点时，出现异常，异常信息:{}",e.getMessage());
        }
        return null;
    }

    /**
     * 创建永久类型的节点
     *
     * @param data 节点数据
     * @param path 节点路径
     * */
    public void createOrReplacePersistent(String path,Serializable data){
        this.create(path,1,data,CreateMode.PERSISTENT);
    }

    public void deletePersistent(String path){
        this.delete(path);
    }

    public void deleteEphemeral(String path){
        this.delete(path);
    }

    /**
     * 创建临时类型的节点
     *
     * @param data 节点数据
     * @param path 节点路径
     * */
    public void createOrReplaceEphemeral(String path,Serializable data){
        this.create(path,1,data,CreateMode.EPHEMERAL);
    }

    /**
     * 创建永久类型的节点
     *
     * @param data 节点数据
     * @param path 节点路径
     * */
    public void createOrUpdatePersistent(String path,Serializable data){
        this.create(path,2,data,CreateMode.PERSISTENT);
    }

    /**
     * 创建临时类型的节点
     *
     * @param data 节点数据
     * @param path 节点路径
     * */
    public void createOrUpdateEphemeral(String path,Serializable data){
        this.create(path,2,data,CreateMode.EPHEMERAL);
    }

    /**
     * 创建带序列临时类型的节点
     *
     * @param data 节点数据
     * @param path 节点路径
     * */
    public void createOrUpdateEphemeralSequential(String path,Serializable data){
        this.create(path,2,data,CreateMode.EPHEMERAL_SEQUENTIAL);
    }


    /**
     * 节点路径
     *
     * @param path 节点路径
     * @param insertType 录入方式，1:existsDelte,2:existsUpdate
     *
     * */
    private void create(String path,int insertType,Serializable data,CreateMode model){
        try {
            this.createByPath(path);
            if(this.exists(path)){
                if(insertType == 1){
                    this.client.delete().forPath(path);
                    this.client.create().withMode(model).forPath(path,this.toByteArray(data));
                }else if(data!=null){
                    this.client.setData().forPath(path,this.toByteArray(data));
                }
            }else{
                this.client.create().withMode(model).forPath(path,this.toByteArray(data));
            }
        } catch (Exception e) {
            logger.info("创建节点时，出现异常，异常信息:{}",e.getMessage());
        }
    }

    private void createByPath(String pathString){
        StringBuilder pathFull = new StringBuilder("");
        String[] pathItems = pathString.split("/");
        for(int i = 0;i<pathItems.length-1;i++){
            if(StringKit.isNotEmpty(pathItems[i])){
                String path = pathFull.append("/").append(pathItems[i]).toString();
                if(!this.exists(path)){
                    try {
                        this.client.create().withMode(CreateMode.PERSISTENT).forPath(path,null);
                    } catch (Exception e) {
                        logger.info("创建前置节点出现异常，路径信息{}，前置节点{}，异常信息:{}",pathString,pathFull.toString(),e.getMessage());
                    }
                }
            }
        }
    }

    /**
     * 删除节点
     * @param path 地址
     */
    private void delete(String path){
        try {
            this.client.delete().forPath(path);
        } catch (Exception e) {
            logger.info("删除节点出现异常，异常信息:{}",e.getMessage());
        }
    }

    /**
     * 判断节点是否存在，如果存在那么就删除
     *
     * @param path 节点路径
     * */
    public void ifExistsThenDelete(String path){
        try {
            if(this.exists(path))this.client.delete().forPath(path);
        } catch (Exception e) {
            logger.info("存在就删除的操作出现异常，异常信息:{}",e.getMessage());
        }
    }

    public boolean exists(String path){
        try {
            return this.client.checkExists().forPath(path)!=null;
        } catch (Exception e) {
            logger.info("存在就删除的操作出现异常，异常信息:{}",e.getMessage());
        }
        return false;
    }

    /**
     * 获取path路径下的子节点上绑定的数据集合
     *
     * @param path 父节点
     * */
    public <E> List<E> getChildrenData(String path,Class<E> eClass){
        List<E> returnList = new ArrayList<>();
        List<String> pathList = this.getChildren(path);
        if(pathList != null)
            for(String p : pathList){
                E object = this.getObject(path + "/" + p,eClass);
                if(object!=null) returnList.add(object);
            }

        return returnList;
    }

    /**
     * 获取某一节点上绑定的数据
     *
     * @param path 节点路径
     * */
    public <E> E getObject(String path,Class<E> eClass){
        try {
            if(this.exists(path)) {
                byte[] data = this.client.getData().forPath(path);
                return this.toObject(data,eClass);
            }
            return null;
        } catch (Exception e) {
            logger.info("获取节点数据，出现异常，异常信息:{}",e.getMessage());
        }
        return null;
    }

    /**
     * 对象转数组
     */
    private byte[] toByteArray(Object obj) throws UnsupportedEncodingException {
        if(obj == null) return null;
        String json = JSONObject.toJSONString(obj);
        return json.getBytes("utf-8");
    }

    /**
     * 数组转对象
     */
    private <T> T toObject(byte[] bytes,Class<T> tClass) throws UnsupportedEncodingException {
        if(bytes == null) return null;
        String json = new String(bytes,"utf-8");
        if(StringKit.isEmpty(json)) return null;
        return JSONObject.parseObject(json,tClass);
    }

    public String getConnectionString() {
        return connectionString;
    }

    public void setConnectionString(String connectionString) {
        this.connectionString = connectionString;
    }

    public String getNamespace() {
        return namespace;
    }

    public void setNamespace(String namespace) {
        this.namespace = namespace;
    }
}
